﻿using FlexFlow.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.IO;
using Newtonsoft.Json.Linq;
using FlexFlow.Base.Interface;

namespace FlexFlow
{
    /// <summary>
    /// 表示工作流编译器
    /// </summary>
    public class WorkFlowProcessIntaller
    {
        /// <summary>
        /// 获取当前编译器绑定的工作流应用程序池
        /// </summary>
        public IApplicationPool BindApplicationPool { get; private set; }
        /// <summary>
        /// 构建活动
        /// </summary>
        /// <param name="obj">
        /// 活动定义Json对象
        /// </param>
        /// <param name="isStart">
        /// 输出值，表示当前活动是否为起始互动
        /// </param>
        /// <returns>
        /// 构建的活动实例
        /// </returns>
        private Activity BuildActivity(JObject obj, Process proc, out bool isStart)
        {
            //新建活动
            Activity act = new Activity();
            act.Name = (string)obj["name"];
            act.Type = ((string)obj["type"]).Split(' ')[0];
            isStart = (act.Type == "start");
            //读取设置
            JObject optObj = (JObject)obj["opt"];
            if (optObj == null)
            {
                return act;
            }
            act.Tag = (string)optObj["tag"];
            act.Operators = (string)optObj["operators"];
            act.Organizations = (string)optObj["organizations"];
            var formTempDef = optObj["formTemplate"];
            Dictionary<string, string> optDic = new Dictionary<string, string>();
            foreach (var opt in optObj)
            {
                optDic[opt.Key] = opt.Value.ToString();
            } 
            act.Options = optDic;
            if (formTempDef != null && formTempDef.Value<String>() != "")
            {
                act.FormTemplate = (Guid)formTempDef;
                //构建流程进入表单数据槽
                DataSlot procEnterFormSlot = SystemDataSlots.ProcessEnterFormDataSlot((Guid)formTempDef);
                //取得活动应用程序
                IWorkFlowApplication app = this.BindApplicationPool.GetApplication(proc.Identity, act.Type);
                if (app == null)
                {
                    throw new Exception("未绑定类型为 " + act.Type + " 的活动的应用程序");
                }

                DataSlot[] slots = app.GetDataSlots(optDic.ToArray());
                //将插槽绑定到活动中
                act.DataSlots = slots.Union(new DataSlot[] { procEnterFormSlot }).ToArray();
            }
            return act;
        }
        /// <summary>
        /// 构建路径
        /// </summary>
        /// <param name="obj">
        /// 路径定义Json对象
        /// </param>
        /// <param name="activityCache">
        /// 已经构建的活动集合，用来做路径连接
        /// </param>
        /// <returns>
        /// 构建的路径实例
        /// </returns>
        private FlowPath BuildPath(JObject obj, Dictionary<string, Activity> activityCache)
        {
            //取得左右活动节点的ID
            string leftAct = (string)obj["from"];
            string rightAct = (string)obj["to"];
            //取得json中的附加选项定义
            JToken optToken = null;
            obj.TryGetValue("opt", out optToken);
            //取得json附加选项定义中定义的流转条件
            JToken flowCondition = null;
            JToken flowVal = null;
            if (optToken!=null)
            {
                JObject opt = optToken as JObject;
                opt.TryGetValue("flowCondition", out flowCondition);
                //string flowOper = (string)obj["flowOper"];
                opt.TryGetValue("flowVal", out flowVal);
            }
            //构造路径实例
            FlowPath path = new FlowPath();
            path.From = activityCache[leftAct].Identity;
            path.To = activityCache[rightAct].Identity;
            if (flowCondition != null && flowVal != null)
            {
                //流转条件：条件值
                path.FlowCondition = ((string)flowCondition).Trim() + ":" + ((string)flowVal).Trim();
            }
            return path;
        }
        /// <summary>
        /// 安装流程
        /// </summary>
        /// <param name="processJson">
        /// 流程Json定义
        /// </param>
        /// <param name="proc">
        /// 安装后的流程
        /// </param>
        public void Install(String processJson, Process proc)
        {
            Dictionary<string, Activity> buildedActivitys = new Dictionary<string, Activity>();
            JObject jobj = JObject.Parse(processJson);
            //取得json定义的节点集合与路径集合
            var nodes = jobj["nodes"];
            var paths = jobj["lines"];
            proc.Activitys = new List<Activity>();
            proc.Paths = new List<FlowPath>();
            //将节点json转换成节点实例
            foreach (JProperty obj in nodes.OfType<JProperty>())
            {
                if (obj.Value is JObject)
                {
                    bool isStart = false;
                    Activity act = this.BuildActivity((JObject)obj.Value, proc, out isStart);
                    if (isStart)
                    {
                        if (proc.StartActivity != Guid.Empty)
                        {
                            throw new Exception("单个流程的起始节点只能有一个！");
                        }
                        proc.StartActivity = act.Identity;
                    }
                    act.ProcessId = proc.Identity;
                    proc.Activitys.Add(act);
                    buildedActivitys.Add(obj.Name, act);
                }
            }
            //将路径json转换成路径实例
            foreach (JProperty obj in paths.OfType<JProperty>())
            {
                if (obj.Value is JObject)
                {
                    FlowPath path = this.BuildPath((JObject)obj.Value, buildedActivitys);
                    path.ProcessId = proc.Identity;
                    proc.Paths.Add(path);
                }
            }
            if (proc.StartActivity == null)
            {
                throw new Exception("流程必须有一个开始节点！");
            }
        }

        public WorkFlowProcessIntaller(IApplicationPool appPoll)
        {
            this.BindApplicationPool = appPoll;
        }
    }
}
